package net.ncraft.mc.easyframework.protocol;

import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.ListenerPriority;
import com.comphenix.protocol.events.PacketAdapter;
import com.comphenix.protocol.events.PacketEvent;
import net.ncraft.mc.easyframework.FunctionRegister;
import net.ncraft.mc.easyframework.context.ApplicationContext;
import net.ncraft.mc.easyframework.context.anno.Autowired;
import net.ncraft.mc.easyframework.context.anno.Bean;
import net.ncraft.mc.easyframework.context.anno.Component;
import net.ncraft.mc.easyframework.protocol.anno.Packet;
import net.ncraft.mc.easyframework.protocol.anno.PacketDirection;
import net.ncraft.mc.easyframework.protocol.anno.PacketListener;
import net.ncraft.mc.easyframework.protocol.anno.ProtocolController;
import org.bukkit.plugin.java.JavaPlugin;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class ProtocolAutoRegister implements FunctionRegister {

    @Autowired
    private JavaPlugin plugin;

    @Autowired
    private ApplicationContext context;


    @Autowired
    private ProtocolManager protocolManager;


    @Override
    public void register() {
        List<Class<?>> protocols = context.getComponents().stream().filter(c -> c.isAnnotationPresent(ProtocolController.class)).toList();
        for (Class<?> protocol : protocols) {
            Map<String, PacketType> packets = Arrays.stream(protocol.getDeclaredFields())
                    .filter(f -> f.isAnnotationPresent(Packet.class))
                    .collect(Collectors.toMap(f -> f.getAnnotation(Packet.class).value(), f -> {
                        try {
                            f.setAccessible(true);
                            return (PacketType) f.get(context.getBean(protocol));
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(e);
                        }
                    }));
            List<Method> listeners = Arrays.stream(protocol.getMethods()).filter(m -> m.isAnnotationPresent(PacketListener.class)).toList();

            for (Method listener : listeners) {
                PacketListener listenerAnnotation = listener.getAnnotation(PacketListener.class);
                String id = listenerAnnotation.value();
                ListenerPriority priority = listenerAnnotation.priority();
                PacketDirection direction = listenerAnnotation.direction();

                if (direction == PacketDirection.SEND) {
                    protocolManager.addPacketListener(new PacketAdapter(plugin, priority, packets.get(id)) {
                        @Override
                        public void onPacketSending(PacketEvent event) {
                            Parameter[] parameters = listener.getParameters();
                            if (!parameters[0].getType().equals(PacketEvent.class)) {
                                throw new RuntimeException("The first parameter of the packet listener must be PacketEvent");
                            }
                            try {
                                listener.invoke(context.getBean(protocol), event);
                            } catch (InvocationTargetException | IllegalAccessException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    });
                }

                if (direction == PacketDirection.RECEIVE) {
                    protocolManager.addPacketListener(new PacketAdapter(plugin, priority, packets.get(id)) {
                        @Override
                        public void onPacketReceiving(PacketEvent event) {
                            Parameter[] parameters = listener.getParameters();
                            if (!parameters[0].getType().equals(PacketEvent.class)) {
                                throw new RuntimeException("The first parameter of the packet listener must be PacketEvent");
                            }
                            try {
                                listener.invoke(context.getBean(protocol), event);
                            } catch (InvocationTargetException | IllegalAccessException e) {
                                throw new RuntimeException(e);
                            }
                        }
                    });
                }
            }
        }
    }


    @Bean(ignoreMethodName = true)
    public ProtocolManager protocolManager() {
        return ProtocolLibrary.getProtocolManager();
    }


}
